#include <Debug.h>
#include <Proc.h>
#include <PIT.h>
#include <stdlib.h>
#include <string.h>

static pid_t lastPID = 1;
Process *Proc::proctab[PROCTAB_SIZE];
static int Processes;

Process *Proc::GetKernelProc() {
	return proctab[0];
}

Process *Proc::GetProc(pid_t pid) {
	Scheduler::Lock.Lock();
	for (int i = 0; i < PROCTAB_SIZE; i++) {
		if ((proctab[i]) && (proctab[i]->pid == pid)) {
			Scheduler::Lock.Unlock();
			return proctab[i];
		}
	}
	Scheduler::Lock.Unlock();
	return NULL;
}

void Proc::Add(Process *p) {
	Scheduler::Lock.Lock();
	for (int i = 0; i < PROCTAB_SIZE; i++) {
		if (!proctab[i]) {
			proctab[i] = p;
			Processes++;
			Scheduler::Lock.Unlock();
			return;
		}
	}
	Scheduler::Lock.Unlock();
}

void Proc::Delete(Process *p) {
	Scheduler::Lock.Lock();
	for (int i = 0; i < PROCTAB_SIZE; i++) {
		if (proctab[i] == p) {
			proctab[i] = NULL;
			Processes--;
			Scheduler::Lock.Unlock();
			return;
		}
	}
	Scheduler::Lock.Unlock();
}

pid_t Proc::Spawn(Process *p) {
	Scheduler::Lock.Lock();

	if (!p->parent)
		p->parent = Scheduler::Current;

	p->ticks = p->priority;
	if (p->pid <= 0) {
		p->pid = lastPID++;
		p->starttime = PIT::Ticks;
	}
	p->state = psReady;
	((List *) p)->Init();
	Scheduler::ReadyList.Add((List *) p);
	Scheduler::Lock.Unlock();
	return p->pid;
}

pid_t Proc::CreateThread(void *entry, char *name) {
	Process *p = new Process;
	memset(p, 0, sizeof(Process));
	strcpy(p->name, name);
	CreateCTX(p, entry);
	Add(p);
	return Spawn(p);
}

void idle() {
	while (1)
		CPU::Halt();
}

int Proc::Stat(ProcessInfo *info) {
	if (!info) {
		return Processes;
	}

	Scheduler::Lock.Lock();
	for (int i = 0, j = 0; i < PROCTAB_SIZE; i++) {
		if (!proctab[i])
			continue;
		info[j].pid = proctab[i]->pid;
		strcpy(info[j].name, proctab[i]->name);
		info[j].ppid = proctab[i]->parent->pid;
		info[j].nice = PRIO2NICE(proctab[i]->priority);
		info[j].state = proctab[i]->state;
		info[j].tms = proctab[i]->tms;
		j++;
	}
	Scheduler::Lock.Unlock();
	return Processes;
}

void Proc::Init() {
	Scheduler::ReadyList.Init();

	// Create IDLE process
	Process *idle = new Process;
	memset(idle, 0, sizeof(idle));
	
	idle->pid = lastPID++;
	idle->state = psRunning;
	idle->priority = NICE2PRIO(19);
	strcpy(idle->name, KERNEL_FILE_NAME",idle");
	idle->parent = idle;
	
	((List *) idle)->Init();
	Scheduler::ReadyList.Add((List *) idle);
	Scheduler::Current = idle;
	Scheduler::Next = idle;

	Scheduler::Initialized = true;
	
	// Add to process table
	Add(idle);
}

#ifdef __x86__
void Proc::CreateCTX(Process *p, void *entry) {
	// Setup kernel stack
	p->kstack = new char[0x1000];
	p->ctx = (struct CPUContext *) ((dword) p->kstack + 0x1000 - sizeof(CPUContext));
	memset(p->ctx, 0, sizeof(CPUContext));

	p->ctx->gs = 0x10;
	p->ctx->fs = 0x10;
	p->ctx->es = 0x10;
	p->ctx->ds = 0x10;

	p->ctx->eflags = 0x202;

	p->ctx->cs = 0x08;
	p->ctx->eip = (dword) entry;
}
#endif
